﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows.Forms;

using RegexEditor.Properties;

namespace RegexEditor
{
	/// <summary>
	/// The main editor form with expression editing capabilities.
	/// </summary>
	public partial class MainForm : BaseForm
	{
		#region Private Fields

		private DateTime _lastKeyStroke;
		private SavedExpression _currentExpression;
		private bool _isDirty;

		#endregion

		#region Constructors

		public MainForm()
		{
			InitializeComponent();
			LoadTabs();
		}

		#endregion

		#region Public Members

		/// <summary>
		/// Gets the current expression.
		/// </summary>
		public SavedExpression CurrentExpression
		{
			get
			{
				if(_currentExpression == null)
					_currentExpression = new SavedExpression();

				SynchronizeExpression();
				return _currentExpression;
			}
		}

		/// <summary>
		/// Gets the current Regex execution context.
		/// </summary>
		public RegexExecution CurrentExecutionContext { get; set; }

		/// <summary>
		/// Gets or sets the delay for live updating.
		/// </summary>
		public TimeSpan LiveUpdateDelay { get; set; }

		/// <summary>
		/// Begins deferred execution, starting the update timer.
		/// </summary>
		public void BeginDeferredExecution()
		{
			updateTimer.Enabled = true;
		}

		/// <summary>
		/// Begins the execution process, starting the background worker.
		/// </summary>
		/// <remarks>
		/// This method has no effect if the background worker is already executing.
		/// </remarks>
		/// <param name="ignoreMaximumMatches">Indicates whether maximum matches should be ignored.</param>
		public void BeginRegexExecution(bool ignoreMaximumMatches)
		{
			// Fire the processing background thread
			if (!backgroundWorker.IsBusy)
				backgroundWorker.RunWorkerAsync(new RegexExecution
				{
					Pattern = patternTextBox.Text,
					Text = textToMatchTextBox.Text,
					Replace = replaceEmptyCheckBox.Checked ? null : replaceTextBox.Text,
					Options = RegexOptions,
					MatchMultipleStrings = matchLineOptionItem.Checked,
					IgnoreMaximumMatches = ignoreMaximumMatches
				});

			updateTimer.Enabled = false;
		}

		/// <summary>
		/// Loads an expression document within the editor.
		/// </summary>
		/// <param name="expression">The expression document.</param>
		public void LoadExpression(SavedExpression expression)
		{
			if (IsDirty && (!string.IsNullOrEmpty(patternTextBox.Text) || !string.IsNullOrEmpty(textToMatchTextBox.Text)))
				if (CurrentExpression.ExpressionId == expression.ExpressionId && CurrentExpression.ExpressionId != Guid.Empty)
				{
					if (MessageBox.Show("Restore the expression to its saved state? This will undo any unsaved changes.", "Question", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No)
						return;
				}
				else if (Settings.Default.AutoSave || MessageBox.Show("Do you want to save the current expression?", "Question", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
					OnSaveExpression(this, EventArgs.Empty);

			topTabs.SelectedIndex = 0;

			if (expression.OneStringPerLine)
				matchLineOptionItem.Checked = true;

			patternTextBox.Text = expression.Pattern;
			patternTextBox.SelectionStart = expression.Pattern != null ? expression.Pattern.Length : 0;
			patternTextBox.Focus();

			titleTextBox.Text = expression.Title;
			textToMatchTextBox.Text = expression.TextToMatch;
			replaceTextBox.Text = expression.ReplacePattern;

			ignoreCaseOptionItem.Checked = (expression.Options & RegexOptions.IgnoreCase) != 0;
			ignoreWhitespaceOptionItem.Checked = (expression.Options & RegexOptions.IgnorePatternWhitespace) != 0;
			multilineOptionItem.Checked = (expression.Options & RegexOptions.Multiline) != 0;
			singleLineOptionItem.Checked = (expression.Options & RegexOptions.Singleline) != 0;
			rightToLeftOptionItem.Checked = (expression.Options & RegexOptions.RightToLeft) != 0;
			explicitCaptureOptionItem.Checked = (expression.Options & RegexOptions.ExplicitCapture) != 0;
			compiledOptionItem.Checked = (expression.Options & RegexOptions.Compiled) != 0;

			_currentExpression = expression;
			SynchronizeExpression();
			IsDirty = false;

			BeginDeferredExecution();
		}

		/// <summary>
		/// Reloads interface components with the current Regex execution context.
		/// </summary>
		public void RefreshInterface()
		{
			if (CurrentExecutionContext != null)
				OnRegexExecutionCompleted(CurrentExecutionContext);
		}

		#endregion

		#region Protected Members

		/// <summary>
		/// Gets or sets the currently active text box control.
		/// </summary>
		protected HighlightTextBox CurrentTextBox { get; set; }

		/// <summary>
		/// Gets or sets whether the editor is currently in a modified state.
		/// </summary>
		protected bool IsDirty
		{
			get { return _isDirty; }
			set
            {
				_isDirty = value;
            	Text = "RegexLab .NET";

				if (_isDirty)
					Text += " *";
            }
		}

		/// <summary>
		/// Gets or sets the current highlighting mode.
		/// </summary>
		protected HighlightingMode HighlightMode
		{
			get
			{
				if (highlightGroupsViewItem.Checked)
					return HighlightingMode.Group;
				else if (highlightMatchesViewItem.Checked)
					return HighlightingMode.Match;
				else if (highlightSplitsViewItem.Checked)
					return HighlightingMode.Split;

				return HighlightingMode.Match;
			}

			set
			{
				switch (value)
				{
					case HighlightingMode.Group:
						highlightGroupsViewItem.Checked = true;
						break;
					case HighlightingMode.Split:
						highlightSplitsViewItem.Checked = true;
						break;
					default:
						highlightMatchesViewItem.Checked = true;
						break;
				}
			}
		}

		/// <summary>
		/// Gets or sets the current highlighting speed.
		/// </summary>
		protected LiveUpdateSpeed HighlightUpdateSpeed
		{
			get
			{
				if (updateDisabledViewItem.Checked)
					return LiveUpdateSpeed.Disabled;
				else if (updateSlowViewItem.Checked)
					return LiveUpdateSpeed.Slow;
				else if (updateNormalViewItem.Checked)
					return LiveUpdateSpeed.Normal;
				else if (updateFastViewItem.Checked)
					return LiveUpdateSpeed.Fast;

				return LiveUpdateSpeed.Normal;
			}

			set
			{
				switch (value)
				{
					case LiveUpdateSpeed.Disabled:
						updateDisabledViewItem.Checked = true;
						break;
					case LiveUpdateSpeed.Slow:
						updateSlowViewItem.Checked = true;
						break;
					case LiveUpdateSpeed.Fast:
						updateFastViewItem.Checked = true;
						break;
					default:
						updateNormalViewItem.Checked = true;
						break;
				}
			}
		}

		/// <summary>
		/// Gets the currently selected Regex options.
		/// </summary>
		protected RegexOptions RegexOptions
		{
			get
			{
				RegexOptions options = RegexOptions.None;

				if (ignoreCaseOptionItem.Checked)
					options |= RegexOptions.IgnoreCase;
				if (ignoreWhitespaceOptionItem.Checked)
					options |= RegexOptions.IgnorePatternWhitespace;
				if (multilineOptionItem.Checked)
					options |= RegexOptions.Multiline;
				if (singleLineOptionItem.Checked)
					options |= RegexOptions.Singleline;
				if (rightToLeftOptionItem.Checked)
					options |= RegexOptions.RightToLeft;
				if (cultureInvariantOptionItem.Checked)
					options |= RegexOptions.CultureInvariant;
				if (explicitCaptureOptionItem.Checked)
					options |= RegexOptions.ExplicitCapture;
				if (compiledOptionItem.Checked)
					options |= RegexOptions.Compiled;

				return options;
			}
		}

		/// <summary>
		/// Fires when the form is being loaded.
		/// </summary>
		/// <param name="e"></param>
		protected override void OnLoad(EventArgs e)
		{
			LoadSettings();
			base.OnLoad(e);
		}

		/// <summary>
		/// Fires when the background worker has finished regex execution.
		/// </summary>
		/// <param name="executionContext"></param>
		protected virtual void OnRegexExecutionCompleted(RegexExecution executionContext)
		{
			int matchCount = executionContext.Matches.Count();
			bool displayMatches = executionContext.IgnoreMaximumMatches || matchCount < Settings.Default.MaximumMatchTreshold;

			// Display errors
			patternErrorLabel.Visible = !string.IsNullOrEmpty(executionContext.ErrorMessage);
			patternErrorLabel.Text = executionContext.ErrorMessage;

			if (bottomTabs.SelectedTab == textToMatchTab && displayMatches)
			{
				if (highlightMatchesViewItem.Checked)
					// Highlight matches
					textToMatchTextBox.Highlight(executionContext.Matches);
				else if (highlightGroupsViewItem.Checked)
					// Highlight groups
					textToMatchTextBox.Highlight(executionContext.Groups);
				else if (highlightSplitsViewItem.Checked)
					// Highlight splits
					textToMatchTextBox.Highlight(executionContext.SplitNodes.Select(n => (StringSegment)n.Tag));
			}
			else if (bottomTabs.SelectedTab == replacedTextTab)
				replacedTextBox.Text = executionContext.ReplacedText;

			// Generate code
			OnCodeGenerationStyleChanged(this, EventArgs.Empty);

			if (rightTabs.SelectedTab == analyzeTab)
				// Display Regex Workbench pattern analysis
				interpretTextBox.Text = executionContext.AnalysisDescription;
			else if (rightTabs.SelectedTab == splitTab && displayMatches)
			{
				using (new PaintLock(splitTreeView))
				{
					// Populate the split tree
					splitTreeView.Nodes.Clear();

					if (executionContext.SplitNodes != null)
						foreach (TreeNode node in executionContext.SplitNodes)
							splitTreeView.Nodes.Add(node);
				}
			}
			else if (rightTabs.SelectedTab == matchTab && displayMatches)
			{
				using (new PaintLock(matchTreeView))
				{
					// Populate the match tree
					matchTreeView.Nodes.Clear();

					if (executionContext.MatchNodes != null)
						foreach (TreeNode node in executionContext.MatchNodes)
							matchTreeView.Nodes.Add(node);
				}
			}

			// Update the status bar
			durationStatusLabel.Text = string.Format("Execution duration: {0}", ((double)executionContext.Duration.TotalMilliseconds / 1000d));

			if (matchCount > 0)
				matchCountStatusLabel.Text = string.Format("{0:N0} {1}", matchCount, matchCount > 1 ? "matches" : "match");
			else
				matchCountStatusLabel.Text = "No match";

			if (!displayMatches)
				messageStatusLabel.Text = "Warning: too many matches for live update. Use the \"Execute\" button to perform matching.";
			else
				messageStatusLabel.Text = "";

			// Save the execution state
			CurrentExecutionContext = executionContext.ErrorMessage == null ? executionContext : null;
		}

		/// <summary>
		/// Initiates live updating of the interface, if live updating is enabled.
		/// </summary>
		protected virtual void OnBeginLiveUpdate(object sender, EventArgs e)
		{
			if (!updateDisabledViewItem.Checked)
				BeginRegexExecution(false);
		}

		/// <summary>
		/// Initiates the manual execution of the regex, discarding any restriction from live updating.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		protected virtual void OnExecuteRegex(object sender, EventArgs e)
		{
			BeginRegexExecution(true);
		}

		/// <summary>
		/// Loads the application settings.
		/// </summary>
		protected virtual void LoadSettings()
		{
			if (Settings.Default.AutoSaveCurrent)
			{
				SavedExpression currentExpression = SavedExpression.FromXmlString(Settings.Default.CurrentExpression);

				if (currentExpression != null)
					LoadExpression(currentExpression);
			}

			LoadSavedExpressions();
			LoadRecentExpressions();

			HighlightMode = (HighlightingMode)Settings.Default.HighlightMode;
			HighlightUpdateSpeed = (LiveUpdateSpeed)Settings.Default.HighlightSpeed;
			Location = Settings.Default.FormLocation;
			WindowState = Settings.Default.FormWindowState;
			horizontalSplit.SplitterDistance = Settings.Default.HorizontalSplitter_Distance;
			verticalSplit.SplitterDistance = Settings.Default.VerticalSplitter_Distance;
			generatedCodeStyleDropDown.SelectedIndex = Settings.Default.GeneratedCodeStyle;
		}

		/// <summary>
		/// Saves the application settings.
		/// </summary>
		protected virtual void SaveSettings()
		{
			Settings.Default.HighlightSpeed = (int)HighlightUpdateSpeed;
			Settings.Default.HighlightMode = (int)HighlightMode;
			Settings.Default.SynchronizeTabs = synchronizeTabsViewItem.Checked;
			Settings.Default.ShowSideTools = viewSideToolsItem.Checked;
			Settings.Default.HorizontalSplitter_Distance = horizontalSplit.SplitterDistance;
			Settings.Default.VerticalSplitter_Distance = verticalSplit.SplitterDistance;
			Settings.Default.TopTabs_SelectedIndex = topTabs.SelectedIndex;
			Settings.Default.BottomTabs_SelectedIndex = bottomTabs.SelectedIndex;
			Settings.Default.GeneratedCodeStyle = generatedCodeStyleDropDown.SelectedIndex;
			Settings.Default.CurrentExpression = CurrentExpression.ToXElement().ToString();
			Settings.Default.FormLocation = Location;
			Settings.Default.FormWindowState = WindowState;

			Settings.Default.Save();
		}

		protected void LoadSavedExpressions()
		{
			using(new PaintLock(savedExpressionsPanel))
			{
				savedExpressionsPanel.Controls.Clear();

				foreach (SavedExpression expression in SavedExpression.SavedExpressions.OrderByDescending(exp => exp.LastUpdate))
				{
					LinkLabel title = new LinkLabel
					{
						AutoSize = true,
						Text = expression.Title,
						Tag = expression,
						Padding = new Padding(0, 8, 0, 4)
					};

					title.Click += (sender, e) => { LoadExpression(((Control)sender).Tag as SavedExpression); };
					savedExpressionsPanel.Controls.Add(title);

					savedExpressionsPanel.Controls.Add(new Label
					{
						AutoSize = true,
						Font = Settings.Default.SideTools_Font,
						Text = expression.Pattern.Truncate(200)
					});
				}
			}
		}
		
		#endregion

		#region Private Members

		private IList<InterfaceTab> InterfaceTabs { get; set; }

		private void LoadRecentExpressions()
		{
			openFileItem.DropDownItems.Clear();

			foreach (SavedExpression savedExpression in SavedExpression.SavedExpressions.OrderByDescending(exp => exp.LastUpdate).Take(Settings.Default.SaveHistorySize))
			{
				var item = new ToolStripMenuItem
				{
					Text = savedExpression.Title,
					Tag = savedExpression,
				};

				item.Click += (sender, e) => { LoadExpression(((ToolStripMenuItem)sender).Tag as SavedExpression); };
				openFileItem.DropDownItems.Add(item);
			}

			if (openFileItem.DropDownItems.Count == 0)
				openFileItem.DropDownItems.Add(new ToolStripMenuItem
				{
					Text = "No recent expression saved",
					Enabled = false
				});
			else
			{
				openFileItem.DropDownItems.Add(new ToolStripSeparator());
				ToolStripMenuItem showAllItem = new ToolStripMenuItem
				{
					Text = "Show all saved expressions"
				};
				showAllItem.Click += (sender, e) =>
				{
					if (horizontalSplit.Panel2Collapsed)
					{
						viewSideToolsItem.Checked = true;
						OnShowSideToolsChanged(sender, e);
					}

					rightTabs.SelectedIndex = 3;
					savedExpressionsPanel.Focus();
				};
				openFileItem.DropDownItems.Add(showAllItem);
			}
		}

		private void LoadTabs()
		{
			if (InterfaceTabs == null)
				InterfaceTabs = new List<InterfaceTab>();

			InterfaceTabs.Clear();
			InterfaceTabs.Add(new InterfaceTab { Page = patternTab, RelatedPage = textToMatchTab, ChildControl = patternTextBox });
			InterfaceTabs.Add(new InterfaceTab { Page = replaceTab, RelatedPage = replacedTextTab, ChildControl = replaceTextBox });
			InterfaceTabs.Add(new InterfaceTab { Page = searchTab, RelatedPage = searchResultsTab, ChildControl = searchRegexLibControl });
			InterfaceTabs.Add(new InterfaceTab { Page = textToMatchTab, RelatedPage = patternTab, ChildControl = textToMatchTextBox });
			InterfaceTabs.Add(new InterfaceTab { Page = replacedTextTab, RelatedPage = replaceTab, ChildControl = replacedTextBox });
			InterfaceTabs.Add(new InterfaceTab { Page = searchResultsTab, RelatedPage = searchTab, ChildControl = searchResultsPanel });
		}

		private void SynchronizeExpression()
		{
			_currentExpression.Title = titleTextBox.Text;
			_currentExpression.Pattern = patternTextBox.Text;
			_currentExpression.ReplacePattern = replaceTextBox.Text;
			_currentExpression.TextToMatch = textToMatchTextBox.Text;
			_currentExpression.Options = RegexOptions;
			_currentExpression.OneStringPerLine = matchLineOptionItem.Checked;
		}

		private void OnUpdateTimerTick(object sender, EventArgs e)
		{
			if (DateTime.Now - _lastKeyStroke > LiveUpdateDelay)
				OnBeginLiveUpdate(sender, e);
		}

		private void OnBackgroundWorkerStart(object sender, DoWorkEventArgs e)
		{
			((RegexExecution)e.Argument).Execute();
			e.Result = e.Argument;
		}

		private void OnBackgroundWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
		{
			OnRegexExecutionCompleted((RegexExecution)e.Result);
		}

		private void OnMatchNodeSelected(object sender, TreeViewEventArgs e)
		{
			Capture capture = e.Node.Tag as Capture;
			textToMatchTextBox.Select(capture);
		}

		private void OnCodeGenerationStyleChanged(object sender, EventArgs e)
		{
			string patternFormat, patternString, patternOptions = string.Empty;
			IEnumerable<string> options = RegexOptions.ToString().Split(',').Select(s => "RegexOptions." + s.Trim());

			switch (generatedCodeStyleDropDown.SelectedIndex)
			{
				case 1:
					patternFormat = "private static readonly Regex _pattern = new Regex(@\"{0}\", {1});";
					patternString = CurrentExpression.Pattern.Replace("\"", "\"\"");
					patternOptions = string.Join(" | ", options);
					break;

				case 2:
					patternFormat = "private static readonly Lazy<Regex> _pattern = new Lazy<Regex>(() => new Regex(@\"{0}\", {1}));";
					patternString = CurrentExpression.Pattern.Replace("\"", "\"\"");
					patternOptions = string.Join(" | ", options);
					break;

				case 3:
					patternFormat = "Dim pattern As Regex = New Regex(\"{0}\", {1})";
					patternString = CurrentExpression.Pattern.Replace("\"", "\"\"").Replace("\n", "\" + _\n\"");
					patternOptions = string.Join(" Or ", options);
					break;

				case 4:
					patternFormat = "Private Shared pattern As Regex = New Regex(\"{0}\", {1})";
					patternString = CurrentExpression.Pattern.Replace("\"", "\"\"").Replace("\n", "\" + _\n\"");
					patternOptions = string.Join(" Or ", options);
					break;

				case 5:
					patternFormat = "var pattern = /{0}/{1};";
					patternString = CurrentExpression.Pattern.Replace("/", "\\/");
					if (RegexOptions.HasFlag(RegexOptions.IgnoreCase))
						patternOptions += "i";
					if (RegexOptions.HasFlag(RegexOptions.Multiline))
						patternOptions += "m";
					break;

				default:
					patternFormat = "Regex pattern = new Regex(@\"{0}\", {1});";
					patternString = CurrentExpression.Pattern.Replace("\"", "\"\"");
					patternOptions = string.Join(" | ", options);
					break;
			}

			generatedCodeTextBox.Text = string.Format(patternFormat, patternString, patternOptions);
		}

		private void OnTabChanged(object sender, EventArgs e)
		{
			var tabsLock = NamedLock.AcquireLock("ChangingTabs");

			if (tabsLock != null)
				using (tabsLock)
				{
					TabControl tabControl = sender as TabControl;
					TabPage currentPage = tabControl.SelectedTab;
					bool synchronized = synchronizeTabsViewItem.Checked;

					if (synchronized)
					{
						InterfaceTab interfaceTab = InterfaceTabs.FirstOrDefault(tab => tab.Page == currentPage);

						if (interfaceTab != null)
						{
							if (interfaceTab.RelatedPage != null)
								((TabControl)interfaceTab.RelatedPage.Parent).SelectedTab = interfaceTab.RelatedPage;
							if (interfaceTab.ChildControl != null)
								interfaceTab.ChildControl.Focus();
						}
					}

					tabControl.SelectedTab = currentPage;
					RefreshInterface();
				}
		}

		private void OnTextBoxEnter(object sender, EventArgs e)
		{
			CurrentTextBox = (HighlightTextBox)sender;
		}

		private void OnTextBoxLeave(object sender, EventArgs e)
		{
			CurrentTextBox = null;
		}

		private void OnTextBoxTextChanged(object sender, EventArgs e)
		{
			// Timestamp the last keystroke for triggering the live update
			_lastKeyStroke = DateTime.Now;
			IsDirty = true;

			BeginDeferredExecution();
		}

		private void OnTextBoxSelectionChanged(object sender, EventArgs e)
		{
			TextBoxBase textBox = (TextBoxBase)sender;

			if (textBox.SelectionLength > 0)
				selectionStatusLabel.Text = string.Format("Selection: {0};{1}", textBox.SelectionStart, textBox.SelectionLength);
			else
				selectionStatusLabel.Text = string.Format("Selection: {0}", textBox.SelectionStart);
		}

		private void OnRegexOptionsChanged(object sender, EventArgs e)
		{
			IsDirty = true;
			OnBeginLiveUpdate(sender, e);
		}

		private void OnUndo(object sender, EventArgs e)
		{
			if (CurrentTextBox != null)
				CurrentTextBox.Undo2();
		}

		private void OnRedo(object sender, EventArgs e)
		{
			if (CurrentTextBox != null)
				CurrentTextBox.Redo2();
		}

		private void OnCut(object sender, EventArgs e)
		{
			if (CurrentTextBox != null)
				CurrentTextBox.Cut();
		}

		private void OnCopy(object sender, EventArgs e)
		{
			if (CurrentTextBox != null)
				CurrentTextBox.Copy();
		}

		private void OnPaste(object sender, EventArgs e)
		{
			if (CurrentTextBox != null && Clipboard.GetDataObject().GetDataPresent(DataFormats.Text))
				CurrentTextBox.Paste();
		}

		private void OnSelectAll(object sender, EventArgs e)
		{
			if (CurrentTextBox != null)
			{
				CurrentTextBox.SelectionStart = 0;
				CurrentTextBox.SelectionLength = CurrentTextBox.Text.Length;
			}
		}

		private void OnDeleteSelection(object sender, EventArgs e)
		{
			if (CurrentTextBox != null)
			{
				if (CurrentTextBox.SelectionLength == 0)
					CurrentTextBox.SelectionLength = 1;

				CurrentTextBox.SelectedText = string.Empty;
			}
		}

		private void OnRegexEscape(object sender, EventArgs e)
		{
			if (CurrentTextBox != null && CurrentTextBox.SelectionLength > 0)
				CurrentTextBox.SelectedText = Regex.Escape(CurrentTextBox.SelectedText);
		}

		private void OnRegexUnescape(object sender, EventArgs e)
		{
			if (CurrentTextBox != null && CurrentTextBox.SelectionLength > 0)
				CurrentTextBox.SelectedText = Regex.Unescape(CurrentTextBox.SelectedText);
		}

		private void OnHighlightingChanged(object sender, EventArgs e)
		{
			var highlightLock = NamedLock.AcquireLock("ChangingHighlight");

			if(highlightLock != null)
				using(highlightLock)
				{
					highlightMatchesViewItem.Checked = false;
					highlightGroupsViewItem.Checked = false;
					highlightSplitsViewItem.Checked = false;
					((ToolStripMenuItem)sender).Checked = true;
					RefreshInterface();
				}
		}

		private void OnLiveUpdateSpeedChanged(object sender, EventArgs e)
		{
			var updateSpeedLock = NamedLock.AcquireLock("ChangingLiveUpdateSpeed");

			if (updateSpeedLock != null)
				using (updateSpeedLock)
				{
					updateSlowViewItem.Checked = false;
					updateNormalViewItem.Checked = false;
					updateFastViewItem.Checked = false;
					updateDisabledViewItem.Checked = false;
					((ToolStripMenuItem)sender).Checked = true;

					if(updateSlowViewItem.Checked)
						LiveUpdateDelay = TimeSpan.FromSeconds(3);
					else if(updateNormalViewItem.Checked)
						LiveUpdateDelay = TimeSpan.FromSeconds(1);
					else if(updateFastViewItem.Checked)
						LiveUpdateDelay = TimeSpan.FromMilliseconds(100);
				}
		}

		private void OnMatchModeChanged(object sender, EventArgs e)
		{
			var matchModeLock = NamedLock.AcquireLock("ChangingMatchMode");

			if (matchModeLock != null)
				using (matchModeLock)
				{
					IsDirty = true;
					matchSingleStringOptionItem.Checked = false;
					matchLineOptionItem.Checked = false;
					((ToolStripMenuItem)sender).Checked = true;
					BeginRegexExecution(false);
				}
		}

		private void OnSetPatternView(object sender, EventArgs e)
		{
			topTabs.SelectedTab = patternTab;
			bottomTabs.SelectedTab = textToMatchTab;
			patternTextBox.Focus();
		}

		private void OnSetReplaceView(object sender, EventArgs e)
		{
			topTabs.SelectedTab = replaceTab;
			bottomTabs.SelectedTab = replacedTextTab;
			replaceTextBox.Focus();
		}

		private void OnSetSearchRegexLibView(object sender, EventArgs e)
		{
			topTabs.SelectedTab = searchTab;
			bottomTabs.SelectedTab = searchResultsTab;
			searchRegexLibControl.Focus();
		}

		private void OnShowSideToolsChanged(object sender, EventArgs e)
		{
			horizontalSplit.Panel2Collapsed = !viewSideToolsItem.Checked;
		}

		private void OnNewExpression(object sender, EventArgs e)
		{
			LoadExpression(new SavedExpression());
			BeginDeferredExecution();
		}

		private void OnSaveExpression(object sender, EventArgs e)
		{
			if (!string.IsNullOrEmpty(patternTextBox.Text) || !string.IsNullOrEmpty(textToMatchTextBox.Text))
			{
				UseWaitCursor = true;

				if (string.IsNullOrEmpty(titleTextBox.Text))
					titleTextBox.Text = patternTextBox.Text.Truncate(50);

				CurrentExpression.Save();
				titleTextBox.Text = CurrentExpression.Title;
				LoadSavedExpressions();
				LoadRecentExpressions();
				UseWaitCursor = false;
				IsDirty = false;
			}
			else
				MessageBox.Show("Current expression is empty.", "Unable to save expression", MessageBoxButtons.OK, MessageBoxIcon.Warning);
		}

		private void OnDuplicateExpression(object sender, EventArgs e)
		{
			if (string.IsNullOrEmpty(patternTextBox.Text) && string.IsNullOrEmpty(textToMatchTextBox.Text))
			{
				MessageBox.Show("Current expression is empty.", "Unable to duplicate expression", MessageBoxButtons.OK, MessageBoxIcon.Warning);
				return;
			}

			if (CurrentExpression.ExpressionId == Guid.Empty)
				if (MessageBox.Show("This expression has not been saved yet. Do you want to save the expression instead?", "Expression not saved", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
				{
					OnSaveExpression(sender, e);
					return;
				}
				else
					return;

			LoadExpression(CurrentExpression.Duplicate());
		}

		private void OnRemoveExpression(object sender, EventArgs e)
		{
			if (MessageBox.Show("Are you sure you want to remove the current expression?", "Confirmation", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == System.Windows.Forms.DialogResult.Yes)
			{
				CurrentExpression.Remove();
				LoadSavedExpressions();
				LoadRecentExpressions();
				OnNewExpression(sender, e);
			}
		}

		private void OnExit(object sender, EventArgs e)
		{
			Close();
		}

		private void OnFormClosed(object sender, FormClosedEventArgs e)
		{
			SaveSettings();
		}

		private void OnShowPreferences(object sender, EventArgs e)
		{
			using (PreferencesForm prefsForm = new PreferencesForm(this))
			{
				prefsForm.ShowDialog();
				LoadSavedExpressions();
				LoadRecentExpressions();
			}
		}

		private void OnSplitSegmentNodeSelected(object sender, TreeViewEventArgs e)
		{
			StringSegment segment = e.Node.Tag as StringSegment;

			if (segment != null)
			{
				textToMatchTextBox.SelectionStart = segment.Index;
				textToMatchTextBox.SelectionLength = segment.Length;
			}
		}

		private void OnLaunchReference(object sender, EventArgs e)
		{
			Process.Start(Settings.Default.ReferenceUrl);
		}

		private void OnShowAboutForm(object sender, EventArgs e)
		{
			(new AboutForm()).ShowDialog();
		}

		private void OnCopyGeneratedCode(object sender, EventArgs e)
		{
			Clipboard.SetText(generatedCodeTextBox.Text);
		}

		private void OnSubmitToRegexLib(object sender, EventArgs e)
		{
			Process.Start("http://www.regexlib.com/Add.aspx");
		}

		private void OnCopyGeneratedCodeCS(object sender, EventArgs e)
		{
			generatedCodeStyleDropDown.SelectedIndex = 0;
			OnCopyGeneratedCode(sender, e);
			MessageBox.Show("Expression has been copied to the clipboard.", "Confirmation", MessageBoxButtons.OK, MessageBoxIcon.Information);
		}

		private void OnCopyGeneratedCodeVB(object sender, EventArgs e)
		{
			generatedCodeStyleDropDown.SelectedIndex = 3;
			OnCopyGeneratedCode(sender, e);
			MessageBox.Show("Expression has been copied to the clipboard.", "Confirmation", MessageBoxButtons.OK, MessageBoxIcon.Information);
		}

		private void OnCopyGeneratedCodeJS(object sender, EventArgs e)
		{
			generatedCodeStyleDropDown.SelectedIndex = 5;
			OnCopyGeneratedCode(sender, e);
			MessageBox.Show("Expression has been copied to the clipboard.", "Confirmation", MessageBoxButtons.OK, MessageBoxIcon.Information);
		}

		private void OnCodePickerChanged(object sender, EventArgs e)
		{
			copyCodeButton.Text = ((ToolStripMenuItem)sender).Text;
			copyCodeButton.Tag = ((ToolStripMenuItem)sender).Tag;
			OnCopyCodeSplit(sender, e);
		}

		private void OnCopyCodeSplit(object sender, EventArgs e)
		{
			generatedCodeStyleDropDown.SelectedIndex = Convert.ToInt32(copyCodeButton.Tag);
			OnCopyGeneratedCode(sender, e);
		}

		private void OnReplaceEmptyStringChanged(object sender, EventArgs e)
		{
			 // TODO: Refactor

			if (replaceEmptyCheckBox.Checked)
			{
				replaceTextBox.Text = "";
				replaceTextBox.ReadOnly = true;
			}
			else
			{
				replaceTextBox.ReadOnly = false;
				OnTextBoxTextChanged(replaceTextBox, EventArgs.Empty);
			}
		}

		private void OnBrowseRegexLib(object sender, EventArgs e)
		{
			Process.Start("http://www.regexlib.com/");
		}

		#endregion

		#region Nested Types

		protected class InterfaceTab
		{
			public TabPage Page { get; set; }
			public TabPage RelatedPage { get; set; }
			public Control ChildControl { get; set; }
		}

		public enum HighlightingMode
		{
			Match,
			Group,
			Split
		}

		public enum LiveUpdateSpeed
		{
			Disabled = 0,
			Slow = 1,
			Normal = 2,
			Fast = 3
		}

		#endregion
	}

}
